package offerbook;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;

/**
 * @date 2019/11/12 0012 下午 4:04
 * 给定一个数组和滑动窗口的大小，找出所有滑动窗口里数值的最大值。
 * 例如，如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3，那么一共存在6个滑动窗口，
 * 他们的最大值分别为{4,4,6,6,6,5}；
 * 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个：
 * {[2,3,4],2,6,2,5,1}， {2,[3,4,2],6,2,5,1}， {2,3,[4,2,6],2,5,1}，
 * {2,3,4,[2,6,2],5,1}， {2,3,4,2,[6,2,5],1}， {2,3,4,2,6,[2,5,1]}。
 */
public class Code65_SlidingWindowMax {
    /**
     * 方式1：遍历.通过测试
     */
    public ArrayList<Integer> maxInWindows1(int [] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        if(num == null || num.length == 0 || size<1){
            return res;
        }
        int max = 0;
        //滑动窗口的起点， 最后的位置是 num.len - size,到num.len-1的长度是size
        for (int i = 0; i <= num.length - size; i++) {
            //在从起点到size的位置，寻找max
            // 从i到i+size-1总size个
            max = num[i];
            for (int j = i + 1; j <i+size ; j++) {
                max = Math.max(max,num[j]);
            }
            res.add(max);
        }
        return res;
    }

    /**
     * 方式2：双端队列，解析见笔记
     */
    public static ArrayList<Integer> maxInWindows2(int[] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        if(num == null || num.length == 0 || size<1 || num.length < size){
            return res;
        }
        //队列按照非递减存储数组的索引，队列头部始终是窗口的最大值
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < num.length; i++) {
            //1更新双端队列，把比num[i]小于等于的都出队
            while (!deque.isEmpty() && num[deque.peekLast()] <= num[i])
                deque.pollLast();
            deque.offer(i);
            //2判断是否过期。当前位置是i，那么窗口起始点为 i-size + 1
            if(deque.peek() == i - size) deque.pollFirst();

            //3检查是否形成窗口，若是则更新结果。起始点是i,终止点
            if(i >= size -1){
                res.add(num[deque.peek()]);
            }
        }

        return res;
    }

    public static void main(String[] args) {
        int []arr = {2,3,4,2,6,2,5,1};
        maxInWindows2(arr,3).forEach(System.out::println);
    }
}


